const natural = require('natural');
const Exercise = require('../models/Exercise');
const AiModelService = require('./aiModelService');

/**
 * 练习题生成服务
 * 负责基于知识卡片生成不同类型的练习题
 */
class ExerciseGenerator {
  /**
   * 为知识卡片生成练习题
   * @param {Object} card - 知识卡片对象
   * @param {Object} options - 选项
   * @param {number} options.choiceCount - 要生成的选择题数量
   * @param {number} options.fillCount - 要生成的填空题数量
   * @param {number} options.codeCount - 要生成的代码题数量
   * @returns {Promise<Array>} - 生成的练习题数组
   */
  static async generateExercises(card, options = {}) {
    const { choiceCount = 1, fillCount = 1, codeCount = 0 } = options;
    const exercises = [];
    
    try {
      // 优先使用AI模型生成练习题
      console.log(`[ExerciseGenerator] 尝试使用AI模型为卡片 ${card._id} 生成练习题`);
      const aiExercises = await this.generateExercisesWithAI(card, options);
      
      if (aiExercises && aiExercises.length > 0) {
        console.log(`[ExerciseGenerator] AI模型成功生成了 ${aiExercises.length} 道练习题`);
        return aiExercises;
      }
      
      console.log(`[ExerciseGenerator] AI模型生成失败，使用传统方法生成练习题`);
    } catch (error) {
      console.error(`[ExerciseGenerator] AI模型生成练习题出错:`, error);
      console.log(`[ExerciseGenerator] 回退到传统方法生成练习题`);
    }
    
    // 如果AI生成失败，使用传统方法
    // 生成选择题
    for (let i = 0; i < choiceCount; i++) {
      const choiceExercise = await this.generateChoiceExercise(card);
      if (choiceExercise) {
        exercises.push(choiceExercise);
      }
    }
    
    // 生成填空题
    for (let i = 0; i < fillCount; i++) {
      const fillExercise = await this.generateFillExercise(card);
      if (fillExercise) {
        exercises.push(fillExercise);
      }
    }
    
    // 生成代码题（如果内容包含代码相关内容）
    if (this.containsCodeContent(card.content)) {
      for (let i = 0; i < codeCount; i++) {
        const codeExercise = await this.generateCodeExercise(card);
        if (codeExercise) {
          exercises.push(codeExercise);
        }
      }
    }
    
    return exercises;
  }

  /**
   * 使用AI模型生成练习题
   * @param {Object} card - 知识卡片对象
   * @param {Object} options - 选项
   * @returns {Promise<Array>} - 生成的练习题数组
   */
  static async generateExercisesWithAI(card, options = {}) {
    const { choiceCount = 1, fillCount = 1, codeCount = 0 } = options;
    const exercises = [];
    
    try {
      // 准备卡片内容
      const cardData = {
        title: card.title,
        content: card.content,
        tags: card.tags,
        difficulty: card.difficulty
      };
      
      // 构建提示词
      const customPrompt = `请基于以下知识卡片内容，生成高质量的练习题，帮助学习者巩固所学知识。

知识卡片:
标题: ${cardData.title}
内容: ${cardData.content}
标签: ${cardData.tags.join(', ')}
难度: ${cardData.difficulty}

请生成以下类型的练习题:
- 选择题: ${choiceCount} 道
- 填空题: ${fillCount} 道
${this.containsCodeContent(cardData.content) ? `- 代码题: ${codeCount} 道` : ''}

要求:
1. 练习题必须直接基于卡片内容，确保答案可以从卡片中找到
2. 选择题必须有一个正确答案和三个干扰项，干扰项要合理且具有迷惑性
3. 填空题应该选择卡片中的关键概念或术语作为填空内容
4. 代码题应该基于卡片中的代码示例，考查关键概念的应用
5. 每道题目都必须包含详细的解释，说明为什么正确答案是正确的
6. 难度应该与卡片难度相匹配

必须返回有效的JSON格式数组，每个练习题包含以下字段:
- question: 题目内容
- type: 题目类型，可选值为"choice"(选择题)、"fill"(填空题)、"code"(代码题)
- options: 选项数组(仅选择题需要)，每个选项包含id(A,B,C,D)和text(选项内容)
- answer: 正确答案(选择题为选项ID，填空题和代码题为答案文本)
- difficulty: 难度级别，可选值为1-5的数字或"EASY"、"MEDIUM"、"HARD"
- explanation: 答案解释

示例输出格式:
[
  {
    "question": "以下关于变量作用域的描述，哪一项是正确的？",
    "type": "choice",
    "options": [
      {"id": "A", "text": "局部变量可以在任何函数中访问"},
      {"id": "B", "text": "全局变量只能在声明它的函数中访问"},
      {"id": "C", "text": "全局作用域中声明的变量可以在整个程序中访问"},
      {"id": "D", "text": "变量作用域与声明方式无关"}
    ],
    "answer": "C",
    "difficulty": "EASY",
    "explanation": "全局作用域中声明的变量可以在整个程序中访问，这是变量作用域的基本概念。局部变量只能在特定函数或代码块中访问，全局变量可以在任何地方访问。"
  },
  {
    "question": "填空题：变量________是指变量在程序中可被访问的范围。",
    "type": "fill",
    "options": [],
    "answer": "作用域",
    "difficulty": "EASY",
    "explanation": "变量作用域是指变量在程序中可被访问的范围，这是编程中的基础概念，用于控制变量的可见性和生命周期。"
  }
]

请注意：只返回纯文本JSON，不要返回HTML或Markdown格式。`;

      // 调用AI模型
      const response = await AiModelService.parseWithQwenLong(JSON.stringify(cardData), {
        task: 'generate_exercises',
        temperature: 0.5,
        max_tokens: 1500,
        customPrompt: customPrompt
      });
      
      if (!response.success) {
        console.error(`[ExerciseGenerator] AI模型生成练习题失败: ${response.error}`);
        return [];
      }
      
      // 解析AI响应
      const responseText = response.result;
      console.log(`[ExerciseGenerator] AI返回原始文本，前100字符: ${responseText.substring(0, 100)}...`);
      
      // 提取JSON
      let exercisesData;
      try {
        // 尝试提取代码块中的JSON
        const codeBlockMatch = responseText.match(/```(?:json)?\s*([\s\S]*?)```/);
        if (codeBlockMatch && codeBlockMatch[1]) {
          exercisesData = JSON.parse(codeBlockMatch[1]);
        } else {
          // 尝试直接解析
          exercisesData = JSON.parse(responseText);
        }
      } catch (error) {
        console.error(`[ExerciseGenerator] 解析AI响应JSON失败:`, error);
        
        // 尝试提取JSON部分
        const jsonMatch = responseText.match(/\[\s*\{[\s\S]*\}\s*\]/);
        if (jsonMatch) {
          try {
            exercisesData = JSON.parse(jsonMatch[0]);
          } catch (extractError) {
            console.error(`[ExerciseGenerator] 提取的JSON部分解析失败:`, extractError);
            return [];
          }
        } else {
          return [];
        }
      }
      
      // 验证并保存练习题
      if (Array.isArray(exercisesData)) {
        for (const exerciseData of exercisesData) {
          // 验证必要字段
          if (!exerciseData.question || !exerciseData.type || !exerciseData.answer) {
            continue;
          }
          
          // 创建练习题对象
          const exercise = new Exercise({
            cardId: card._id,
            sourceDocument: card.sourceDocument,
            question: exerciseData.question,
            type: exerciseData.type,
            options: exerciseData.options || [],
            answer: exerciseData.answer,
            difficulty: exerciseData.difficulty || card.difficulty,
            explanation: exerciseData.explanation || `正确答案是: ${exerciseData.answer}`
          });
          
          // 保存练习题
          try {
            await exercise.save();
            exercises.push(exercise);
          } catch (saveError) {
            console.error(`[ExerciseGenerator] 保存练习题失败:`, saveError);
          }
        }
      }
      
      return exercises;
    } catch (error) {
      console.error(`[ExerciseGenerator] 使用AI模型生成练习题时出错:`, error);
      return [];
    }
  }

  /**
   * 生成选择题
   * @param {Object} card - 知识卡片对象
   * @returns {Promise<Object|null>} - 生成的选择题或null
   */
  static async generateChoiceExercise(card) {
    try {
      // 提取关键概念
      const concepts = this.extractConcepts(card.content);
      
      if (concepts.length === 0) {
        return null;
      }
      
      // 随机选择一个概念作为问题基础
      const concept = concepts[Math.floor(Math.random() * concepts.length)];
      
      // 生成问题
      const question = this.generateQuestion(concept, card.content);
      
      // 生成选项（包括正确答案和干扰项）
      const correctOption = this.generateCorrectOption(concept, card.content);
      const distractors = await this.generateDistractors(concept, card.content, card.tags);
      
      // 将正确选项和干扰项合并并打乱顺序
      const options = [
        { id: 'A', text: correctOption },
        { id: 'B', text: distractors[0] },
        { id: 'C', text: distractors[1] },
        { id: 'D', text: distractors[2] }
      ].sort(() => Math.random() - 0.5);
      
      // 找出正确答案的ID
      const answer = options.find(option => option.text === correctOption).id;
      
      // 创建练习题对象
      const exercise = new Exercise({
        cardId: card._id,
        question,
        type: 'choice',
        options,
        answer,
        difficulty: card.difficulty,
        explanation: `正确答案是${answer}：${correctOption}。这是因为${this.generateExplanation(concept, card.content)}`
      });
      
      // 保存练习题
      await exercise.save();
      return exercise;
    } catch (error) {
      console.error('生成选择题时出错:', error);
      return null;
    }
  }

  /**
   * 生成填空题
   * @param {Object} card - 知识卡片对象
   * @returns {Promise<Object|null>} - 生成的填空题或null
   */
  static async generateFillExercise(card) {
    try {
      // 提取关键概念
      const concepts = this.extractConcepts(card.content);
      
      if (concepts.length === 0) {
        return null;
      }
      
      // 随机选择一个概念作为填空基础
      const concept = concepts[Math.floor(Math.random() * concepts.length)];
      
      // 从内容中找出包含该概念的句子
      const tokenizer = new natural.SentenceTokenizer();
      const sentences = tokenizer.tokenize(card.content);
      
      const relevantSentences = sentences.filter(sentence => 
        sentence.toLowerCase().includes(concept.toLowerCase())
      );
      
      if (relevantSentences.length === 0) {
        return null;
      }
      
      // 随机选择一个相关句子
      const sentence = relevantSentences[Math.floor(Math.random() * relevantSentences.length)];
      
      // 创建填空题
      const blankedSentence = sentence.replace(new RegExp(concept, 'i'), '________');
      
      // 创建练习题对象
      const exercise = new Exercise({
        cardId: card._id,
        question: `填空题：${blankedSentence}`,
        type: 'fill',
        options: [],
        answer: concept,
        difficulty: card.difficulty,
        explanation: `正确答案是：${concept}。${this.generateExplanation(concept, card.content)}`
      });
      
      // 保存练习题
      await exercise.save();
      return exercise;
    } catch (error) {
      console.error('生成填空题时出错:', error);
      return null;
    }
  }

  /**
   * 生成代码练习题
   * @param {Object} card - 知识卡片对象
   * @returns {Promise<Object|null>} - 生成的代码题或null
   */
  static async generateCodeExercise(card) {
    try {
      // 检查内容是否包含代码
      if (!this.containsCodeContent(card.content)) {
        return null;
      }
      
      // 简单实现：根据卡片内容生成一个简单的代码填空题
      const codeSnippet = this.extractCodeSnippet(card.content);
      
      if (!codeSnippet) {
        return null;
      }
      
      // 从代码片段中选择一个关键部分替换为空白
      const { blankedCode, answer } = this.createCodeBlanks(codeSnippet);
      
      // 创建练习题对象
      const exercise = new Exercise({
        cardId: card._id,
        question: `完成下面的代码：\n\n${blankedCode}`,
        type: 'code',
        options: [],
        answer,
        difficulty: card.difficulty + 1 > 5 ? 5 : card.difficulty + 1, // 代码题难度稍高
        explanation: `正确答案是：${answer}。这段代码的作用是${this.explainCodeSnippet(codeSnippet)}`
      });
      
      // 保存练习题
      await exercise.save();
      return exercise;
    } catch (error) {
      console.error('生成代码题时出错:', error);
      return null;
    }
  }

  /**
   * 提取内容中的关键概念
   * @param {string} content - 内容文本
   * @returns {Array<string>} - 关键概念数组
   */
  static extractConcepts(content) {
    // 使用自然语言处理工具提取关键名词和术语
    const words = content.split(/\s+/);
    const tfidf = new natural.TfIdf();
    
    tfidf.addDocument(content);
    
    // 获取TF-IDF值较高的词
    const terms = [];
    tfidf.listTerms(0).slice(0, 10).forEach(item => {
      // 只选择长度大于2的词
      if (item.term.length > 2) {
        terms.push(item.term);
      }
    });
    
    return terms;
  }

  /**
   * 生成问题
   * @param {string} concept - 关键概念
   * @param {string} content - 内容文本
   * @returns {string} - 生成的问题
   */
  static generateQuestion(concept, content) {
    // 根据概念生成问题
    const questionTemplates = [
      `以下关于${concept}的描述，哪一项是正确的？`,
      `${concept}的主要特点是什么？`,
      `下列哪一项最准确地描述了${concept}？`,
      `关于${concept}，以下说法正确的是？`
    ];
    
    return questionTemplates[Math.floor(Math.random() * questionTemplates.length)];
  }

  /**
   * 生成正确选项
   * @param {string} concept - 关键概念
   * @param {string} content - 内容文本
   * @returns {string} - 生成的正确选项
   */
  static generateCorrectOption(concept, content) {
    // 从内容中找出包含该概念的句子
    const tokenizer = new natural.SentenceTokenizer();
    const sentences = tokenizer.tokenize(content);
    
    const relevantSentences = sentences.filter(sentence => 
      sentence.toLowerCase().includes(concept.toLowerCase())
    );
    
    if (relevantSentences.length > 0) {
      // 使用包含概念的句子作为正确选项
      return relevantSentences[0];
    }
    
    // 如果没有找到相关句子，使用通用描述
    return `${concept}是本文中讨论的重要概念。`;
  }

  /**
   * 生成干扰项
   * @param {string} concept - 关键概念
   * @param {string} content - 内容文本
   * @param {Array<string>} tags - 标签数组
   * @returns {Promise<Array<string>>} - 生成的干扰项数组
   */
  static async generateDistractors(concept, content, tags) {
    // 生成3个干扰项
    const distractors = [];
    
    // 方法1：修改原始正确句子中的关键词
    const correctOption = this.generateCorrectOption(concept, content);
    const modifiedOption = correctOption.replace(
      new RegExp(concept, 'i'),
      tags[Math.floor(Math.random() * tags.length)] || '另一个概念'
    );
    distractors.push(modifiedOption);
    
    // 方法2：使用内容中的其他句子，但不包含目标概念
    const tokenizer = new natural.SentenceTokenizer();
    const sentences = tokenizer.tokenize(content);
    
    const otherSentences = sentences.filter(sentence => 
      !sentence.toLowerCase().includes(concept.toLowerCase())
    );
    
    if (otherSentences.length > 0) {
      distractors.push(otherSentences[Math.floor(Math.random() * otherSentences.length)]);
    } else {
      distractors.push(`${concept}与本文内容无关。`);
    }
    
    // 方法3：创建一个相反的陈述
    const negativeOption = correctOption.includes('不') || correctOption.includes('没有') ?
      correctOption.replace(/不|没有/, '') :
      correctOption.replace(new RegExp(`(${concept})`, 'i'), `不是$1`);
    distractors.push(negativeOption);
    
    // 确保返回3个干扰项
    while (distractors.length < 3) {
      distractors.push(`${concept}的定义与本文所述不符。`);
    }
    
    return distractors;
  }

  /**
   * 生成解释
   * @param {string} concept - 关键概念
   * @param {string} content - 内容文本
   * @returns {string} - 生成的解释
   */
  static generateExplanation(concept, content) {
    // 从内容中提取与概念相关的句子作为解释
    const tokenizer = new natural.SentenceTokenizer();
    const sentences = tokenizer.tokenize(content);
    
    const relevantSentences = sentences.filter(sentence => 
      sentence.toLowerCase().includes(concept.toLowerCase())
    );
    
    if (relevantSentences.length > 0) {
      return relevantSentences.slice(0, 2).join(' ');
    }
    
    return `这与${concept}的定义相符。`;
  }

  /**
   * 检查内容是否包含代码相关内容
   * @param {string} content - 内容文本
   * @returns {boolean} - 是否包含代码内容
   */
  static containsCodeContent(content) {
    // 检查是否包含代码相关关键词或符号
    const codeIndicators = [
      'function', 'class', 'var', 'const', 'let',
      'if', 'for', 'while', 'return', 'import',
      '{', '}', ';', '()', '=>', '=='
    ];
    
    return codeIndicators.some(indicator => content.includes(indicator));
  }

  /**
   * 从内容中提取代码片段
   * @param {string} content - 内容文本
   * @returns {string|null} - 提取的代码片段或null
   */
  static extractCodeSnippet(content) {
    // 尝试找出代码块（简单实现）
    const codeBlockRegex = /```[\s\S]*?```|`[\s\S]*?`|\{[\s\S]*?\}|function[\s\S]*?\}/g;
    const matches = content.match(codeBlockRegex);
    
    if (matches && matches.length > 0) {
      return matches[0].replace(/```/g, '').trim();
    }
    
    return null;
  }

  /**
   * 在代码中创建空白
   * @param {string} codeSnippet - 代码片段
   * @returns {Object} - 包含空白的代码和答案
   */
  static createCodeBlanks(codeSnippet) {
    // 找出代码中的关键词或变量
    const keywordRegex = /\b(function|class|const|let|var|return|if|else|for|while)\b|\b([a-zA-Z_]\w*)\b(?=\s*[=:])/g;
    const matches = Array.from(codeSnippet.matchAll(keywordRegex));
    
    if (matches.length === 0) {
      return { blankedCode: codeSnippet, answer: '' };
    }
    
    // 随机选择一个关键词替换为空白
    const randomMatch = matches[Math.floor(Math.random() * matches.length)];
    const answer = randomMatch[0];
    const blankedCode = codeSnippet.replace(randomMatch[0], '______');
    
    return { blankedCode, answer };
  }

  /**
   * 解释代码片段
   * @param {string} codeSnippet - 代码片段
   * @returns {string} - 代码解释
   */
  static explainCodeSnippet(codeSnippet) {
    // 简单实现：根据代码中的关键词生成解释
    if (codeSnippet.includes('function')) {
      return '定义了一个函数，用于执行特定的操作。';
    } else if (codeSnippet.includes('class')) {
      return '定义了一个类，用于创建对象。';
    } else if (codeSnippet.includes('if') || codeSnippet.includes('else')) {
      return '实现了条件逻辑，根据条件执行不同的代码。';
    } else if (codeSnippet.includes('for') || codeSnippet.includes('while')) {
      return '实现了循环逻辑，重复执行某些操作。';
    }
    
    return '展示了代码的基本结构和语法。';
  }
}

module.exports = ExerciseGenerator; 